package y2017.m10.d19;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;

public class TCPServer {
    public static void main(String[] args) throws Exception {
        // if at least two argument are passed, consider the first one as directory path
        // and the second one as port number
        // If port number is not present, default it to 3333
        // If directory path is not present, show error
        if (args.length == 0) {
            System.out.println("Please enter the server directory address as first argument while running from command line.");
        } else {
            int id = 1;
            System.out.println("Server started...");
            System.out.println("Waiting for connections...");

            ServerSocket welcomeSocket;

            // port number is passed by the user
            if (args.length >= 2) {
                welcomeSocket = new ServerSocket(Integer.parseInt(args[1]));
            } else {
                welcomeSocket = new ServerSocket(3333);
            }

            while (true) {
                Socket connectionSocket = welcomeSocket.accept();
                System.out.println("Client with ID " + id + " connected from " + connectionSocket.getInetAddress().getHostName() + "...");
                Thread server = new ThreadedServer(connectionSocket, args[0]);
                id++;
                server.start();
            }
        }
    }
}

class ThreadedServer extends Thread {
    private Socket connectionSocket;
    private String dirName;

    ThreadedServer(Socket s, String dir) {
        connectionSocket = s;
        // set dirName to the one that's entered by the user
        dirName = dir;
    }

    public void run() {
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
            InputStream inFromClient = connectionSocket.getInputStream();
            OutputStream output = connectionSocket.getOutputStream();

            ObjectOutputStream oout = new ObjectOutputStream(output);
            oout.writeObject("Server says Hi!");

            File ff = new File(dirName);
            String[] list = ff.list();
            if (list != null) {
                ArrayList<String> names = new ArrayList<>(Arrays.asList(list));
                oout.writeObject(String.valueOf(names.size()));
                for (String name : names) {
                    oout.writeObject(name);
                }
                String name1 = in.readLine();
                String ch = name1.substring(0, 1);
                if (ch.equals("*")) {
                    int n = name1.lastIndexOf("*");
                    String filename = name1.substring(1, n);
                    FileInputStream file = null;
                    BufferedInputStream bis = null;
                    boolean fileExists = true;
                    System.out.println("Request to download file " + filename + " recieved from " + connectionSocket.getInetAddress().getHostName() + "...");
                    filename = dirName + filename;
                    //System.out.println(filename);
                    try {
                        file = new FileInputStream(filename);
                        bis = new BufferedInputStream(file);
                    } catch (FileNotFoundException excep) {
                        fileExists = false;
                        System.out.println("FileNotFoundException:" + excep.getMessage());
                    }
                    if (fileExists) {
                        oout = new ObjectOutputStream(output);
                        oout.writeObject("Success");
                        System.out.println("Download begins");
                        sendBytes(bis, output);
                        System.out.println("Completed");
                        bis.close();
                        file.close();
                        oout.close();
                        output.close();
                    } else {
                        oout = new ObjectOutputStream(output);
                        oout.writeObject("FileNotFound");
                        oout.close();
                        output.close();
                    }
                } else {
                    try {
                        boolean complete = true;
                        System.out.println("Request to upload file " + name1 + " recieved from " + connectionSocket.getInetAddress().getHostName() + "...");
                        File directory = new File(dirName);
                        if (!directory.exists()) {
                            if (directory.mkdir()) {
                                System.out.println("Dir made");
                            }
                        }

                        int size = 9022386;
                        byte[] data = new byte[size];
                        File fc = new File(directory, name1);
                        FileOutputStream fileOut = new FileOutputStream(fc);
                        DataOutputStream dataOut = new DataOutputStream(fileOut);

                        while (complete) {
                            int m = inFromClient.read(data, 0, data.length);
                            if (m == -1) {
                                complete = false;
                                System.out.println("Completed");
                            } else {
                                dataOut.write(data, 0, m);
                                dataOut.flush();
                            }
                        }
                        fileOut.close();
                    } catch (Exception exc) {
                        System.out.println(exc.getMessage());
                    }
                }
            }
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }

    private static void sendBytes(BufferedInputStream in, OutputStream out) throws Exception {
        int size = 9022386;
        byte[] data = new byte[size];
        int c = in.read(data, 0, data.length);
        out.write(data, 0, c);
        out.flush();
    }
}